/***
 * * PROJECT
 * *    Logical Neighborhoods - J2ME Run-Time Support 
 * * VERSION
 * *    $LastChangedRevision: 125 $
 * * DATE
 * *    $LastChangedDate: 2008-06-28 14:07:26 +0200 (Sat, 28 Jun 2008) $
 * * LAST_CHANGE_BY
 * *    $LastChangedBy: lmottola $
 * *
 * *    $Id: LNRouting.java 125 2008-06-28 12:07:26Z lmottola $
 * *
 * *   Logical Neighborhoods - 
 * *                   Programming Wireless Sensor Networks
 * *                   (One Slice at a Time)
 * *
 * *   This program is free software; you can redistribute it and/or
 * *   modify it under the terms of the GNU Lesser General Public License
 * *   as published by the Free Software Foundation; either version 2.1
 * *   of the License, or (at your option) any later version.
 * *
 * *   This program is distributed in the hope that it will be useful,
 * *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 * *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * *   GNU General Public License for more details.
 * *
 * *   You should have received a copy of the GNU Lesser General Public 
 * *   License along with this program; if not, you may find a copy at 
 * *   the FSF web site at 'www.gnu.org' or 'www.fsf.org', or you may 
 * *   write to the Free Software Foundation, Inc., 59 Temple 
 * *   Place - Suite 330, Boston, MA  02111-1307, USA
 ***/

package atag.runtime.ln;

import java.util.Hashtable;
import java.util.Vector;

import atag.runtime.ln.neighborhoodDefs.Neighborhood;
import atag.runtime.ln.nodeAttributes.Node;
import atag.runtime.ln.messages.AppMsg;
import atag.runtime.ln.messages.LNMessage;
import atag.runtime.ln.messages.ReplyMsg;
import atag.runtime.ln.neighborhoodDefs.ConjunctiveNeighborhood;

/**
 * @author Luca Mottola <a
 *         href="mailto:mottola@elet.polimi.it">mottola@elet.polimi.it</a>
 * 
 */
public class LNRouting {

	private NeighborSet neighborSet;

	private Hashtable seenMessages;

	private LogicalNeighborhoods ln;

	private Node nodeInfo;

	private byte disseminationFanOut;

	private long appTime;

	public LNRouting(Node info, NeighborSet neighborSet,
			byte disseminationFanOut, LogicalNeighborhoods ln) {

		this.disseminationFanOut = disseminationFanOut;
		this.ln = ln;
		this.nodeInfo = info;
		this.neighborSet = neighborSet;
		this.seenMessages = new Hashtable();
		this.appTime = 0;
	}

	protected void generateAppMsg(byte[] data, Neighborhood[] scopes) {

		AppMsg msg;

		// TODO: Derive overlapping scopes
		for (int i = 0; i < scopes.length; i++) {
			msg = new AppMsg(nodeInfo.getMyId(), appTime++, data, scopes[i]);
			// See if this needs to be handled locally
			parseAppMsg(msg);                        
			
			//also send it out on the network interfaces
			ln.enqueueMessage(msg);
			
		}
	}

	protected void generateReplyMsg(byte[] data, int destination) {

		ln.enqueueMessage(new ReplyMsg(nodeInfo.getMyId(), appTime++, data,
				destination));
	}

	protected void parseAppMsg(AppMsg msg) {

		if (!isAlreadySeen(msg)) {
            {
                ConjunctiveNeighborhood c=(ConjunctiveNeighborhood)msg.getLNScope();
                 System.out.println("send pred"+c.predicates.elementAt(0));
                 System.out.println(c.predicates.elementAt(1));
                 //System.out.println(c.predicates.elementAt(2));
            }
			if (msg.getLNScope().matches(nodeInfo)) {
                nodeInfo.debugPrint("Delivering message from "
						+ msg.getSenderId() + " generated from "
						+ msg.getOriginatorId());
				ln.deliver(msg.getData());
			} 
			if (!msg.isMaxHops() || msg.getHops() < msg.getMaxHops()) {
				nodeInfo.debugPrint("Forwarding message from "
						+ msg.getSenderId() + " generated from "
						+ msg.getOriginatorId());

				Vector destinations = neighborSet
						.returnRandomNeighbors(disseminationFanOut);
				msg.setDestinations(destinations);
				ln.forwardMessage(msg);
			}
		}
	}

	protected void parseReplyMsg(ReplyMsg msg) {

		if (!isAlreadySeen(msg)) {
			if (msg.getDestination() == nodeInfo.getMyId()) {
				ln.deliver(msg.getData());
			}
			nodeInfo.debugPrint("Forwarding reply message from "
					+ msg.getSenderId() + " generated from "
					+ msg.getOriginatorId());
			//Vector destinations = neighborSet
				//	.returnRandomNeighbors(disseminationFanOut);
			//msg.setDestinations(destinations);
			ln.forwardMessage(msg);
		}
	}

	private boolean isAlreadySeen(LNMessage msg) {

		// TODO: Implement limited buffer
		Vector seenForId = (Vector) seenMessages.get(new Integer(msg
				.getOriginatorId()));

		if (seenForId == null) {
			// Never got a message from this node
			seenForId = new Vector();
			seenForId.addElement(new Long(msg.getSeqNumber()));
			seenMessages.put(new Integer(msg.getOriginatorId()), seenForId);
			return false;
		} else {
			if (!seenForId.contains(new Long(msg.getSeqNumber()))) {
				seenForId.addElement(new Long(msg.getSeqNumber()));
				return false;
			} else {
				return true;
			}
		}
	}
}
